package com.sw.haruka.module.music;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadataRetriever;
import android.os.Build;

import androidx.annotation.RequiresApi;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by shqyang on 05/29/2021
 */
public class MusicManager {

    private static MusicManager instance;
    private MusicServer mMusicServer;
    private Collection<Observer> mObservers;

    public static MusicManager getInstance() {
        if (instance == null) {
            instance = new MusicManager();
        }
        return instance;
    }

    private MusicManager() {
        mMusicServer = new MusicServer();
        mObservers = new CopyOnWriteArrayList<>();
    }

    public MusicServer.State getPlayerState() {
        return mMusicServer.getState();
    }

    public void notifyState() {
        for (Observer observer : mObservers) {
            observer.notifyState(mMusicServer.getState(), mMusicServer.isLoop());
        }
    }

    public Music getInfo(String path) {
        Music music = new Music();
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        retriever.setDataSource(path);
        String name = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
        String artist = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
        music.setName(name);
        music.setArtist(artist);
        return music;
    }

    public Bitmap getCover(String filePath) {
        // MediaMetadataRetriever is available on API Level 8
        // but is hidden until API Level 10
        Class<?> clazz = null;
        Object instance = null;
        try {
            clazz = Class.forName("android.media.MediaMetadataRetriever");
            instance = clazz.newInstance();

            Method method = clazz.getMethod("setDataSource", String.class);
            method.invoke(instance, filePath);

            // The method name changes between API Level 9 and 10.
            byte[] data = (byte[]) clazz.getMethod("getEmbeddedPicture").invoke(instance);
            if (data != null) {
                Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                if (bitmap != null) return bitmap;
            }
            return (Bitmap) clazz.getMethod("getFrameAtTime").invoke(instance);
        } catch (IllegalArgumentException ex) {
            // Assume this is a corrupt video file
        } catch (RuntimeException ex) {
            // Assume this is a corrupt video file.
        } catch (java.lang.InstantiationException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        } finally {
            try {
                if (instance != null) {
                    clazz.getMethod("release").invoke(instance);
                }
            } catch (Exception ignored) {
            }
        }
        return null;
    }

    public void setSource(String path) {
        if (path.equals(mMusicServer.getCurrentMusicPath())) {
            notifyState();
            return;
        }
        if (mMusicServer.getState() != MusicServer.State.STOP) {
            mMusicServer.stop();
        }
        mMusicServer.reset();
        mMusicServer.setSource(path);
        notifyState();
    }

    public void switchLoop() {
        mMusicServer.setLoop(!mMusicServer.isLoop());
        notifyState();
    }

    public void setLoop(boolean isLoop) {
        mMusicServer.setLoop(isLoop);
        notifyState();
    }

    public void start() {
        if (mMusicServer.getState() == MusicServer.State.PLAY) {
            return;
        }
        if (mMusicServer.getState() == MusicServer.State.STOP) {
            mMusicServer.prepare();
        }
        mMusicServer.start();
        notifyState();
    }

    public void pause() {
        if (mMusicServer.getState() == MusicServer.State.PAUSE) {
            return;
        }
        mMusicServer.pause();
        notifyState();
    }

    public void stop() {
        if (mMusicServer.getState() == MusicServer.State.STOP) {
            return;
        }
        mMusicServer.stop();
        notifyState();
    }

    public void release() {
        mMusicServer.release();
    }

    public void addObserver(Observer observer) {
        mObservers.add(observer);
    }

    public void removeObserver(Observer observer) {
        mObservers.remove(observer);
    }

    public interface Observer {
        void notifyState(MusicServer.State state, boolean isLoop);
    }
}
